Claude Sonnet


Journaux liées à cette note :

Script d'analyse des coûts Aider à partir de l'historique #astuce, #CodeAssistant

J'ai cherché une fonctionnalité pour calculer le coût total de l'utilisation d'Aider sur un projet. Je n'ai rien trouvé dans la documentation ni dans les issues du projet Aider.

Je sais qu'Aider enregistre toutes les informations de session dans .aider.chat.history.md.

Ce fichier contient des lignes du type : "Tokens: 49k sent, 1.9k received. Cost: $0.17 message, $1.68 session".

À partir de ces informations, j'ai généré avec Claude Sonnet 4.5 le script Python aider_cost_analyzer.py.

Installation sous Fedora :

$ mkdir -p ~/.local/bin
$ curl -o ~/.local/bin/aider_cost_analyzer.py https://gist.githubusercontent.com/stephane-klein/3b3808c1b03b7e6ddfc4b22b69fdf776/raw/4262faec0cb2f468ea6b6d8751339b8e8497f005/aider_cost_analyzer.py
$ chmod +x ~/.local/bin/aider_cost_analyzer.py

Exemple d'utilisation :

$ aider_cost_analyzer.py .
Analyzing 1 history file(s) in '.':

.aider.chat.history.md:
  Messages: 30
  Tokens sent: 639,200 | received: 65,613
  Cost: $1.86

============================================================
Total tokens sent: 639,200
Total tokens received: 65,613
Total cost: $1.86

J'ai posté l'issue suivante dans le repository aider-ce : "Calculate cumulative token usage and cost across all Aider sessions".

net-tools est déprécié, je dois remplacer "lsof -i" par "ss -tlnp" #linux, #kernel, #network

J'utilise habituellement lsof -i | grep "8080" pour identifier le processus qui écoute sur le port 8080.

Comme je le mentionnais dans 2025-07-04_1614, j'ai appris net-tools au début des années 2000 et j'ai gardé cette habitude depuis.

D'après Claude Sonnet 4.5, iproute2 est devenu le standard dans Debian, Ubuntu et Fedora vers 2013-2015. net-tools a été supprimé de l'installation par défaut entre 2017 et 2018.


lsof (pour LiSt Open Files) interroge /proc pour récupérer les informations. Cette implémentation, comme tous les outils net-tools, est ancienne et n'utilise pas l'API moderne Netlink.

En 2025, la méthode recommandée pour identifier les processus écoutant des ports est cette commande iproute2 (code source) :

$ ss -tlnp | grep 8080

Explication des options :

  • -t: TCP sockets
  • -l: Listening sockets uniquement
  • -n: Affichage numérique (pas de résolution DNS)
  • -p: Affiche les processus

J'ai écrit cette note et bridge-utils est déprécié, je dois remplacer "brctl" par "ip link" pour m'aider à adopter les commandes modernes iproute2.


Pour apprendre cette nouvelle commande, j'ai ajouté la carte-mémoire suivante dans Anki :

J'ai découvert class-variance-authority et tailwinds-variants #ReactJS, #svelte, #WebDev, #JaiDécouvert

Jusqu'à présent, pour la gestion conditionnelle des classes CSS dans mes projets ReactJS ou Svelte, j'utilisais clsx.
Pour Svelte en particulier, j'utilise souvent directement le mécanisme conditionnel natif de l'attribut "class" du framework.

Aujourd'hui, dans un projet professionnel ReactJS, #JaiDécouvert la librairie conditionnelle class-variance-authority.

Cette librairie existe depuis debut 2022 et voici un exemple d'utilisation de class-variance-authority :

<script>
  import { cva } from 'class-variance-authority';

  const button = cva(
    'font-semibold rounded', // équivalent au paramètre `base:` utlisé par tailwind-variants 
    {
	    variants: {
	      intent: {
	        primary: 'bg-blue-500 text-white hover:bg-blue-600',
	        secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300'
	      },
	      size: {
	        sm: 'px-3 py-1 text-sm',
	        md: 'px-4 py-2 text-base',
	        lg: 'px-6 py-3 text-lg'
	      }
	    },
	    compoundVariants: [
	      {
	        intent: 'primary',
	        size: 'lg',
	        class: 'uppercase tracking-wide' // <= appliqué seulement si intent="primary" et size="lg"
	      }
	    ],
	    defaultVariants: {
	      intent: 'primary',
	      size: 'md'
	    }
    }
  );

  export let intent = 'primary';
  export let size = 'md';
</script>

<button class={button({ intent, size })}>
  <slot />
</button>

Je trouve cette approche plus élégante que clsx pour des besoins complexes, comme la création d'un design system.


Dans la même famille de librairie, il existe aussi tailwind-merge que j'avais déjà identifié mais sans avoir jamais pris le temps d'étudier. Ses fonctionnalités sont très simples et minimalistes :

Merge Tailwind CSS classes without style conflicts

source


Claude Sonnet 4.5 m'a fait découvrir tailwind-variants, une alternative à class-variance-authority. Cette lib est spécifique à Tailwind CSS mais offre de meilleures performances et des fonctionnalités supplémentaires par rapport à class-variance-authority.
Le projet a été créé début 2023, soit environ 1 an après class-variance-authority.

Voici un tableau comparant les fonctionnalités de tailwind-variants et class-variance-authority :

La dernière section de la page Tailwind Variants - Comparison explique l'historique de la lib avec class-variance-authority et les raisons qui ont motivé sa création.

Après quelques difficultés, je pense avoir saisi l'intérêt de la fonctionnalité "slots", disponible dans tailwind-variants mais absente de class-variance-authority.


Si un jour je dois créer des composants de design system avancés dans un projet ReactJS ou Svelte, je pense que j'utiliserai tailwind-variants.

J'ai découvert Adminer, alternative à PhpMyAdmin et je me suis intéressé à OPCache #php, #mysql, #WebDev, #JaiDécouvert

En travaillant sur un playground d'étude de Podman Quadlets, dans le README.md de l'image Docker mariadb, #JaiDécouvert le projet Adminer (https://www.adminer.org) qui semble être l'équivalent de PhpMyAdmin, mais sous la forme d'un fichier unique.

Je découvre aussi que contrairement à PhpMyAdmin, Adminer n'est pas limité à Mysql / MariaDB, il supporte aussi PostgreSQL.

En regardant le dépôt GitHub d'Adminer, je découvre que le gros fichier PHP de 496 kB est le résultat de la concaténation de nombreux fichiers php.
Ça me rassure, parce que je me demandais comment l'édition d'un fichier unique de cette taille pouvait être humainement gérable.

Je trouve astucieux ce mode de déploiement d'un projet PHP sous forme d'un seul fichier qui me fait penser à la méthode Golang. Cependant, je me pose des questions sur la performance de cette technique étant donné que PHP fonctionne en mode process-per request (CGI), ce qui signifie que ce gros fichier PHP est interprété à chaque action sur la page 🤔.

En creusant un peu le sujet avec Claude Sonnet 4.5, je découvre que depuis la version 5.5 de PHP, OPCache améliore significativement la vitesse des requêtes PHP, sans pour autant atteindre celle de Golang, NodeJS, Python ou Ruby qui utilisent des serveurs HTTP intégrés. La consommation mémoire reste supérieure dans des conditions d'implémentation comparables.
Avec OPCache, Adminer semble rester performant malgré l'utilisation d'un fichier unique.

Script qui permet de transformer automatiquement du texte en anglais en fichier audio #anglais, #astuce

J'ai généré avec Claude Sonnet 4.5 le script Python text_to_audio.py. Il me permet de transformer automatiquement du texte en anglais en fichier audio mp3.
J'intègre ensuite ces fichiers dans mes carte-mémoire Anki pour travailler mon anglais.

Installation sous Fedora :

$ mkdir -p ~/.local/bin
$ curl -o ~/.local/bin/text_to_audio.py https://gist.githubusercontent.com/stephane-klein/1406e746f0253956062d4adff7a692bd/raw/8571cdd91cae8ebcd208435daacf431cfc1cd353/text_to_audio.py
$ chmod +x ~/.local/bin/text_to_audio.py

Exemple d'utilisation :

$ text_to_audio.py "Reinforcement Learning from Human Feedback"
Downloading audio for: 'Reinforcement Learning from Human Feedback'
Language: en-GB
URL: https://translate.google.com/translate_tts?ie=UTF-8&tl=en-GB&client=tw-ob&q=Reinforcement+Learning+from+Human+Feedback
Saving to: /home/stephane/english_audio/2025-12-01_Reinforcement_Learning_from_Human_Feedback.mp3

✓ Successfully downloaded to '/home/stephane/english_audio/2025-12-01_Reinforcement_Learning_from_Human_Feedback.mp3'
File size: 28224 bytes

J'utilise actuellement un endpoint HTTP de Google Translator pour générer les fichiers audio, en attendant de trouver une alternative plus open source / communautaire.

Journal du vendredi 21 novembre 2025 à 14:32 #embeddings, #llm, #JaiDécouvert

Via Claude Sonnet 4.5, #JaiDécouvert le projet Massive Text Embedding Benchmark qui compare les embeddings Models.

Voici le site de documentation, son dépôt GitHub, et son leaderboard qui liste actuellement 319 models, dont 180 supportant le français.

Setup Fedora CoreOS avec LUKS et Tang #linux, #security, #admin-sys, #CoreOS

Il y a quelques jours, dans ma note "Setup Fedora CoreOS avec LUKS et TPM", je disais :

Attention, j'ai découvert que cette méthode n'est pas sécurisée en cas de vol physique du serveur !

Si un attaquant boot depuis un autre disque avec le même firmware et le même kernel, il pourra extraire en clair la clé LUKS stockée dans le TPM 🫣.

source

Une solution pour traiter ce point faible est d'utiliser un pin éloigné physiquement du serveur qui l'utilise.

Le framework Clevis utilise le terme "pins" pour désigner les différents méthodes de déverrouillage d'un volume LUKS.

Origine du mot "pin" ?
Claude Sonnet 4.5 m'a expliqué que le terme "pin", qui se traduit par "goupille" en français, désigne la pièce mécanique qui bloque l'ouverture d'un cadenat.

Par exemple, dans un contexte self hosting dans un homelab, je peux héberger physiquement un serveur dans mon logement et le connecter à un pin sur un serveur Scaleway ou sur un serveur dans le homelab d'un ami.

Les pins distants, accessibles via réseau, sont appelés serveurs Network-Bound Disk Encryption.

Si le serveur Network-Bound Disk Encryption est configuré pour répondre uniquement aux requêtes provenant de l'IP de mon réseau homelab, en cas de vol du serveur, le voleur ne pourra pas récupérer le secret permettant de déchiffrer le volume LUKS.

Dans le playground install-coreos-iso-on-qemu-with-luks-and-tang, j'ai testé avec succès le déverrouillage d'un volume LUKS avec un serveur Network-Bound Disk Encryption nommé tang.

Pour être précis, dans la configuration de ce playground, deux pins sont obligatoires pour déverrouiller automatiquement le volume : un pin tang et un pin TPM2. Le nombre minimum de pins requis pour le déverrouillage est défini par le paramètre threshold.

clevis, qui permet de configurer les pins et de gérer la récupération de la passphrase à partir des pins, utilise l'algorithme Shamir's secret sharing (SSS) pour répartir le secret à plusieurs endroits.

Voici quelques scénarios de conditions de déverrouillage que clevis permet de configurer grâce à SSS :

  • TPM2 ou Tang serveur 1
  • TPM2 et Tang serveur 1
  • Tang serveur 1 ou Tang serveur 2
  • 2 parmi Tang serveur 1, Tang serveur 2, Tang serveur 3
  • ...

Si les conditions ne sont pas remplies, systemd-ask-password demande à l'utilisateur de saisir sa passphrase au clavier.


Je n'ai pas trouvé d'image docker officielle de tang. Toutefois, j'ai trouvé ici l'image non officielle padhihomelab/tang (son dépôt GitHub : https://github.com/padhi-homelab/docker_tang).
Dans mon playground, je l'ai déployé dans ce docker-compose.yml.


J'ai trouvé la configuration butane de tang simple à définir (lien vers le fichier) :

  luks:
    - name: var
      device: /dev/disk/by-partlabel/var
      wipe_volume: true
      key_file:
        inline: password
      clevis:
        tpm2: true
        tang:
          - url: "http://10.0.2.2:1234"
            # $ docker compose exec tang jose jwk thp -i /db/pLWwUuLhqqFb-Mgf5iVkwuV4BehG9vzd2SXGMyGroNw.jwk
            # pLWwUuLhqqFb-Mgf5iVkwuV4BehG9vzd2SXGMyGroNw
            thumbprint: dx9dNzgs-DeXg0SCBQW5rb7WQkSIN1B8MIgcO6WxJfI
        threshold: 2 # TMP2 + Tang (or passphrase keyboard input)

La seule complexité que j'ai rencontrée est la méthode pour récupérer le paramètre thumbprint de l'instance tang.

Voici la méthode que j'ai utilisée :

$ docker compose exec tang jose jwk thp -i /db/pLWwUuLhqqFb-Mgf5iVkwuV4BehG9vzd2SXGMyGroNw.jwk
pLWwUuLhqqFb-Mgf5iVkwuV4BehG9vzd2SXGMyGroNw

Autre difficulté, il faut ajouter les arguments kernel suivants pour activer l'accès réseau dès le début du process de boot afin de permettre à clevis d'accéder au serveur tang :

variant: fcos
version: 1.6.0
kernel_arguments:
  should_exist:
    - ip=dhcp
    - rd.neednet=1


J'ai intégré au README.md du playground une section nommée "How to switch from 2 required ping to 1 or the opposite?", pour documenter comment modifier à chaud la configuration clevis.
Par exemple pour changer de serveur tang ou modifier le nombre de pins nécessaires pour déverrouiller la partition chiffrée.


Il est conseillé d'activer le pin TPM2 en complément de tang pour éviter ce type d'attaque décrit par Claude Sonnet 4.5 :

Imaginons un admin malveillant dans ton entreprise :

  • Il a accès physique aux serveurs
  • Il a accès au réseau (donc au serveur Tang)
  • Il clone un disque pendant la nuit
  • Il essaie de le monter sur sa propre machine pour extraire des données

Tang seul : ❌ Il réussit
TPM + Tang : ✅ Il échoue

Claude Sonnet 4.5


À noter que l'instance tang ne contient pas la passphrase et ne voit jamais passer la passphrase via son API.
Voici les explications que m'a données Claude Sonnet 4.5 :

Tang ne stocke pas la passphrase secrète de chiffrement LUKS. C'est justement tout l'intérêt de cette solution !

Voici comment ça fonctionne :

Le principe de Tang + Clevis

  1. Génération de clé dérivée : Clevis (le client) contacte le serveur Tang et récupère sa clé publique
  2. Chiffrement avec la clé de Tang : Clevis utilise cette clé publique pour chiffrer la passphrase LUKS
  3. Stockage local : La passphrase chiffrée est stockée localement dans l'en-tête LUKS du disque (pas sur le serveur Tang)
  4. Déchiffrement au boot :
  • La machine contacte Tang
  • Tang utilise sa clé privée pour aider à déchiffrer
  • La passphrase LUKS est reconstituée
  • Le disque est déverrouillé

Ce que Tang sait et ne sait pas

  • ❌ Tang ne connaît jamais votre passphrase LUKS
  • ❌ Tang ne stocke rien concernant vos clés
  • ✅ Tang fournit juste un service cryptographique (un oracle de déchiffrement)
  • ✅ C'est un serveur sans état (stateless)

C'est du chiffrement asymétrique avec un mécanisme appelé "network-bound disk encryption" : le disque ne peut être déchiffré que si la machine peut contacter le serveur Tang sur le réseau.

Claude Sonnet 4.5


Voici quelques ressources supplémentaires au sujet des techniques de déverrouillage automatique des volumes LUKS :

J'ai découvert les types "unknown" et "never" en TypeScript #typescript, #javascript, #JaiDécouvert, #JaimeraisUnJour

En TypeScript, dans mon projet professionnel, #JaiDécouvert le type unknown qui ressemble à any mais qui est différent.

Exemple (produit par Claude Sonnet 4.5) avec any :

let value: any;
value.foo.bar(); // No error, even if it crashes at runtime
value.trim(); // No error, even if value is a number

Exemple avec unknown :

let value: unknown;
value.trim(); // Error: Object is of type 'unknown'

// You must narrow the type first
if (typeof value === 'string') {
  value.trim(); // OK, TypeScript knows it's a string
}

unknown a été introduit dans la version 3.0 de TypeScript en 2018 : Announcing TypeScript 3.0 - The unknown type.

J'ai trouvé les réponses à cette question StackOverflow intéressantes : 'unknown' vs. 'any'.

C'est peut-être parce que je ne suis pas habitué à la documentation de TypeScript , mais j'ai l'impression que la fonctionnalité unknown n'est pas correctement documentée. Par exemple, je suis surpris de trouver presque rien à son sujet dans la page Everyday-types , ni dans les chapitres "Reference" :

Et rien non plus dans les tutoriels.

Au passage, j'ai aussi découvert le type never.


#JaimeraisUnJour prendre le temps de parcourir la documentation de TypeScript de manière exhaustive. Jusqu'à présent, je n'en ai jamais eu réellement besoin, car je n'ai jamais contribué à de projet écrit en TypeScript. Mais maintenant, cela devient une nécessité pour mon projet professionnel.

Fusion de CoreOS et Atomic Project en 2018 #CoreOS, #linux, #fedora

Cette note fait partie de la série de notes : "J'ai étudié et testé CoreOS et je suis tombé dans un rabbit hole 🙈".

Note précédente : "2014-2018 approche alternative avec Atomic Project".


Suite au rachat de la société CoreOS par Red Hat en 2018, les projets CoreOS Container Linux et Fedora Atomic Host ont fusionné en juillet 2019 pour donner Fedora CoreOS.

D'après mon analyse, mise à part ignition, le projet Fedora CoreOS est construit sur les bases de Fedora Atomic Host et n'a gardé de CoreOS Container Linux que le nom "CoreOS".

Cette nouvelle distribution Fedora CoreOS reste atomic et immutable comme l'ancien CoreOS Container Linux, mais utilise désormais rpm-ostree et OSTree (au lieu du système dual partition A/B), et permet le package layering si nécessaire. La philosophie "100% conteneurs" reste encouragée, mais n'est plus une contrainte absolue.

Voici une chronologie sur l'histoire de CoreOS que m'a proposée Claude Sonnet 4.5 :

2013-2017: CoreOS Container Linux
           ├─ Atomic ✓ (dual partition)
           ├─ Immutable ✓
           └─ Package layering ✗

2014-2018: Fedora/RHEL Atomic Host
           ├─ Atomic ✓ (OSTree)
           ├─ Immutable ✓
           └─ Package layering ✓ (rpm-ostree)

2018:      Rachat CoreOS par Red Hat

2019+:     Fedora CoreOS (fusion des deux)
           ├─ Atomic ✓ (OSTree)
           ├─ Immutable ✓
           ├─ Package layering ✓ (possible mais découragé)
           └─ Philosophie: conteneurs first, mais flexible

Note suivante : "Quelques outils CoreOS : coreos-installer, graphe de migration et zincati".

Je fais mon retour dans l'écosystème React, j'ai découvert Jotai et Zustand #ReactJS, #WebDev, #svelte, #javascript, #JaiDécouvert, #JaiLu

Dans le code source de mon projet professionnel, #JaiDécouvert la librairie ReactJS nommée Jotai (https://jotai.org).

Les atom de Jotai ressemblent aux fonctionnalités Svelte Store. Jotai permet entre autres d'éviter de faire du props drilling.

Pour en savoir plus sur l'intérêt de Jotai versus "React context (useContext + useState)", je vous conseille la lecture d'introduction de la page Comparison de la documentation Jotai. J'ai trouvé la section "Usage difference" très simple à comprendre.

Cette découverte est une bonne surprise pour moi, car les atom de Jotai reproduisent l'élégance syntaxique des Store de Svelte, ce qui améliore mon confort de développement en ReactJS. #JaiLu ce thread Hacker News en lien avec le sujet : "I like Svelte more than React (it's store management)".

Je tiens toutefois à préciser que si Jotai améliore significativement mon expérience de développeur (DX) avec ReactJS, cela reste une solution de gestion d'état au sein du runtime ReactJS. En comparaison, le compilateur Svelte génère du code optimisé natif qui reste intrinsèquement plus performant à l'exécution.

Exemple Svelte :

import { writable, derived } from 'svelte/store';

const count = writable(0);
const doubled = derived(count, $count => $count * 2);

// Usage dans component
$count // auto-subscription

Exemple ReactJS basé sur Jotai :

import { atom } from 'jotai';

const countAtom = atom(0);
const doubledAtom = atom(get => get(countAtom) * 2);

// Usage dans component
const [count] = useAtom(countAtom);

J'ai lu la page "Comparison" de Jotai pour mieux comprendre la place qu'a Jotai dans l'écosystème ReactJS.

#JaiDécouvert deux autres librairies développées par la même personne, Daishi Kato : Zustand et Valtio. D'après ce que j'ai compris, Daishi a développé ces librairies dans cet ordre :

J'ai aussi découvert Recoil développé par Facebook, mais d'après son entête GitHub celle-ci semble abandonnée. Une migration de Recoil vers Jotai semble être conseillée.

J'aime beaucoup comment Daishi Kato choisit le nom de ses librairies, la méthode est plutôt simple 🙂 :

Jotai means "state" in Japanese. Zustand means "state" in German.

source

Comme mentionné plus haut, Jotai ressemble à Recoil alors que Zustand ressemble à Redux :

Analogy

Jotai is like Recoil. Zustand is like Redux.

...

How to structure state

Jotai state consists of atoms (i.e. bottom-up). Zustand state is one object (i.e. top-down).

source


Même en lisant la documentation Comparison, j'ai eu de grandes difficulté à comprendre quand préférer Zustand à Jotai.
En lisant la documentation, Jotai me semble toujours plus simple à utiliser que Zustand.

Avec l'aide de Claude Sonnet 4.5, je pense avoir compris quand préférer Zustand à Jotai.

Exemple Zustand

Dans l'exemple Zustand suivant, la fonction addToCart modifie plusieurs parties du state useCartStore en une seule transaction :

import { create } from 'zustand'

const useCartStore = create((set) => ({  
	user: null,  
	cart: [],  
	notifications: [],  
    
	addToCart: (product) => set((state) => {  
		return {  
		    cart: [...state.cart, product],  
		    notifications: (  
				state.user
					? [...state.notifications, { type: 'cart_updated' }]
					: state.notifications
			)  
		};
    };  
}));

Et voici un exemple d'utilisation de addToCart dans un composant :

function ProductCard({ product }) {
	// Sélectionner uniquement l'action addToCart
	const addToCart = useCartStore((state) => state.addToCart);
  
	return (
	    <div>
		    <h3>{product.name}</h3>
			<p>{product.price}€</p>
			<button onClick={() => addToCart(product)}>
			    Ajouter au panier
		    </button>
		</div>
	);
}

Exemple Jotai

Voici une implémentation équivalente basée sur Jotai :

import { atom } from 'jotai';

const userAtom = atom(null);
const cartAtom = atom([]);
const notificationsAtom = atom([]);

export const addToCartAtom = atom(
	null,
	(get, set, product) => {
		const user = get(userAtom);
		const cart = get(cartAtom);
		const notifications = get(notificationsAtom);
    
		set(cartAtom, [...cart, product]);
    
		if (user) {
			set(notificationsAtom, [...notifications, { type: 'cart_updated' }]);
		}
	}
);

Et voici un exemple d'utilisation de useToCartAtom dans un composant :

import { useSetAtom } from 'jotai';
import { addToCartAtom } from 'addToCartAtom';

function ProductCard({ product }) {
	// Récupérer uniquement l'action (pas la valeur)
	const addToCart = useSetAtom(addToCartAtom);
  
	return (
		<div>
		    <h3>{product.name}</h3>
		    <p>{product.price}€</p>
		    <button onClick={() => addToCart(product)}>
			    Ajouter au panier
			</button>
	    </div>
	);
}

Ces deux exemples montrent que Zustand est plus élégant et probablement plus performant que Jotai pour gérer des actions qui conditionnent ou modifient plusieurs parties du state simultanément.


#JaiLu le thread SubReddit ReactJS "What do you use for global state management? " et j'ai remarqué que Zustand semble plutôt populaire.

En rédigeant cette note, j'ai découvert Valtio qui semble être une alternative à MobX. Je prévois d'étudier ces deux librairies dans une future note.